In [75]:
recipe = """
[make 1 dot]
[move 1 space]
[turn 1 degree]
[repeat 170 times]
"""

Turtle Shell

Inspired by Lauren Ipsum

  • Directed by Margaret Stokes-Rees
  • Written by Ian Stokes-Rees

In [1]:
import bokeh.plotting as bk
from math import cos, sin
from numpy import deg2rad


BokehJS successfully loaded.

In [2]:
bk.output_notebook()

In [50]:
from functools import wraps

def instruction(func):

    @wraps(func)
    def wrapped(*args, **kwargs):
        extras = {}
        if 'inst' in kwargs:
            parts = kwargs.pop('inst')
            
            for color in 'red green blue pink purple white black'.split():
                if color in parts:
                    extras['color'] = color
                    break
                    
            if 'tiny' in parts:
                extras['size'] = 1
            elif 'small' in parts:
                extras['size'] = 3
            elif 'medium' in parts:
                extras['size'] = 10
            elif 'big' in parts:
                extras['size'] = 20
            elif 'huge' in parts:
                extras['size'] = 40
                
            kwargs.update(extras)
            
        return func(*args, **kwargs)

    return wrapped

In [79]:
debug = False
class Turtle:
    def __init__(self, point=(0,0), 
                       direction=200.0, 
                       turn_angle=1.0,
                       step=1.0):

        self.point = point
        self.direction = direction
        self.direction %= 360.0
        self.turn_angle = turn_angle
        self.step = step

    def turn(self, amount=None):
        if not amount:
            amount = self.turn_angle
            
        self.direction += amount
        
    def right(self):
        self.direction -= 90.0
        self.direction %= 360.0
        
    def left(self):
        self.direction += 90.0
        self.direction %= 360.0
        
    def flip(self):
        self.direction -= 180.0
        self.direction %= 360.0
        
    def move(self, amount=None):
        if not amount:
            amount = self.step
        
        self.last_point = self.point
        self.point = (self.point[0] + self.step*cos(deg2rad(self.direction)),
                      self.point[1] + self.step*sin(deg2rad(self.direction)))

    def goto(self, x, y):
        self.point = (x, y)
        
    @instruction
    def dot(self, color='red', size=10):
        self.fig.scatter([self.point[0]], 
                         [self.point[1]],
                         color=color, s=size)
    
    @instruction
    def line(self, color='blue', size=3):
        self.fig.line([self.last_point[0], self.point[0]],
                      [self.last_point[1], self.point[1]],
                       color=color, size=size)

    def make_recipe(self, recipe):
        self.fig = bk.figure(plot_width=400, plot_height=400,
                             tools = "pan,box_zoom,reset,resize,save",
                             #x_axis_type=None, y_axis_type=None,
                             min_border=0, outline_line_color=None,
                             title="Turtle Shell")
        self.fig.grid.grid_line_color = None
        self.fig.axis.axis_line_color = None
        self.fig.axis.minor_tick_line_color = None
        
        instructions = recipe.splitlines()
        count = self.do(instructions[-1])
        if count:
            instructions.pop() # remove the last instruction
        else:
            count = 1 # there is no repeat, so just do recipe once
        
        for i in range(count):
            if debug: print "Doing {n} instructions:".format(n=len(instructions))
            for instruction in instructions:
                self.do(instruction)
            
        bk.show(self.fig)
        
    def do(self, instruction):
        if not instruction or instruction.startswith('#'):
            return

        if not (instruction.startswith('[') and instruction.endswith(']')):
            print "Skipping bad instruction:\n\t",
            print instruction,
            return
        
        if debug: print "\tinstruction", instruction
        #print "\t\t", self

        # we have a good instruction: figure it out
        parts = instruction[1:-1].split()
        first = parts[0]
        last  = parts[-1]
        rest  = parts[1:]
        
        if first == "repeat" and last == "times":
            return int(parts[1])
        elif first == "turn":
            self.turn()
        elif first == "move":
            self.move()
        elif first == "make":
            if last == "dot":
                self.dot(inst=parts)
            if last == "line":
                self.line(inst=parts)
                
    def __str__(self):
        return ("point({x}, {y}) ".format(x=self.point[0], y=self.point[1]) +
                "direction({d}) ".format(d=self.direction) + 
                "angle({a}) ".format(a=self.turn_angle) +
                "step({s})".format(s=self.step))

In [77]:
t = Turtle()

In [78]:
t.make_recipe(recipe)


{"inputs": { "recipe": "para" } }